﻿/////////////////////////////////////////////////////////////////////////
//
// Copyright © Microsoft Corporation.  All rights reserved.  
// This code is a “supplement” under the terms of the 
// Microsoft Kinect for Windows SDK (Beta) from Microsoft Research 
// License Agreement: http://research.microsoft.com/KinectSDK-ToU
// and is licensed under the terms of that license agreement. 
//
/////////////////////////////////////////////////////////////////////////

using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using Coding4Fun.Kinect.Wpf;
using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox;
using Microsoft.Kinect;

[assembly:CLSCompliant(true)]
namespace Microsoft.Research.Kinect.Samples.CursorControl
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		private const float ClickThreshold = 0.33f;
		private const float SkeletonMaxX = 0.60f;
		private const float SkeletonMaxY = 0.40f;

        private KinectSensor kinectSensor;
		private NotifyIcon _notifyIcon = new NotifyIcon();

        //Use to transform and reduce jitter
        private TransformSmoothParameters TransformParameters = new TransformSmoothParameters
        {
            Smoothing = 0.1f,
            Correction = 0.3f,
            Prediction = 0.4f,
            JitterRadius = 1.0f,
            MaxDeviationRadius = 0.5f
        };

		public MainWindow()
		{
			InitializeComponent();

			// create tray icon
			_notifyIcon.Icon = new System.Drawing.Icon("CursorControl.ico");
			_notifyIcon.Visible = true;
			_notifyIcon.DoubleClick += delegate
			{
				this.Show();
				this.WindowState = WindowState.Normal;
				this.Focus();
			};

            this.kinectSensor = KinectSensor.KinectSensors[0];
		}

		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
            try
            {
                foreach (KinectSensor sensor in KinectSensor.KinectSensors)
                {
                    if (sensor.Status == KinectStatus.Connected)
                    {
                        // Pass parameters to set smoothing
                        sensor.SkeletonStream.Enable(TransformParameters);
                        sensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
                        sensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(KinectAllFramesReady);
                        sensor.DepthStream.Range = DepthRange.Default;
                        sensor.Start();

                        this.kinectSensor = sensor;

                        break;
                    }
                }
            }
            catch (InvalidOperationException)
            {
                MessageBox.Show("Runtime initialization failed. Ensure Kinect is plugged in");
                return;
            }
		}

		private void Window_Closed(object sender, EventArgs e)
		{
			// shut down the Kinect device
			_notifyIcon.Visible = false;
            kinectSensor.Stop();
		}

		private void Window_StateChanged(object sender, EventArgs e)
		{
			if (WindowState == WindowState.Minimized)
				this.Hide();
		}

        Skeleton[] skeletons;

		private void KinectAllFramesReady(object sender, AllFramesReadyEventArgs e)
        {
            // Depth
            using (DepthImageFrame depthImageFrame = e.OpenDepthImageFrame())
            {
                if (depthImageFrame != null)
                {
                    // if the window is displayed, show the depth buffer
                    if (this.WindowState == WindowState.Normal)
                        video.Source = depthImageFrame.ToBitmapSource();
                }
            }

            using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
            {
                bool receivedData = false;

                if (skeletonFrame != null)
                {
                    if (skeletons == null) //allocate the first time
                    {
                        skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
                    }

                    skeletonFrame.CopySkeletonDataTo(skeletons);

                    receivedData = true;
                }
                else
                {
                    // apps processing of skeleton data took too long; it got more than 2 frames behind.
                    // the data is no longer avabilable.
                    receivedData = false;
                }

                if (!receivedData)
                    return;



                foreach (Skeleton skeleton in skeletons)
                {
                    // the first found/tracked skeleton moves the mouse cursor
                    if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
                    {
                        // make sure both hands are tracked
                        if (skeleton.Joints[JointType.HandLeft].TrackingState == JointTrackingState.Tracked &&
                            skeleton.Joints[JointType.HandRight].TrackingState == JointTrackingState.Tracked)
                        {
                            int cursorX, cursorY;

                            // get the left and right hand Joints
                            Joint jointRight = skeleton.Joints[JointType.HandRight];
                            Joint jointLeft = skeleton.Joints[JointType.HandLeft];

                            // scale those Joints to the primary screen width and height
                            Joint scaledRight = jointRight.ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);
                            Joint scaledLeft = jointLeft.ScaleTo((int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, SkeletonMaxX, SkeletonMaxY);

                            // figure out the cursor position based on left/right handedness
                            if (LeftHand.IsChecked.GetValueOrDefault())
                            {
                                cursorX = (int)scaledLeft.Position.X;
                                cursorY = (int)scaledLeft.Position.Y;
                            }
                            else
                            {
                                cursorX = (int)scaledRight.Position.X;
                                cursorY = (int)scaledRight.Position.Y;
                            }

                            bool leftClick;

                            // figure out whether the mouse button is down based on where the opposite hand is
                            if ((LeftHand.IsChecked.GetValueOrDefault() && jointRight.Position.Y > ClickThreshold) ||
                                    (!LeftHand.IsChecked.GetValueOrDefault() && jointLeft.Position.Y > ClickThreshold))
                                leftClick = true;
                            else
                                leftClick = false;

                            Status.Text = cursorX + ", " + cursorY + ", " + leftClick;
                            NativeMethods.SendMouseInput(cursorX, cursorY, (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, leftClick);

                            return;
                        }
                    }
                }
            }
		}
	}
}
